package com.xyy.saas.payment.callback.core.handler;

import com.xyy.saas.payment.api.impl.PaymentOrderApiImpl;
import com.xyy.saas.payment.callback.core.dto.CallbackMsg;
import com.xyy.saas.payment.callback.core.result.CallbackRunner;
import com.xyy.saas.payment.common.Constants;
import com.xyy.saas.payment.common.exception.PaymentException;
import com.xyy.saas.payment.cores.bo.SeparateAccountsBo;
import com.xyy.saas.payment.cores.bo.UserBo;
import com.xyy.saas.payment.cores.constants.CallbackConstants;
import com.xyy.saas.payment.cores.enums.*;
import com.xyy.saas.payment.cores.vo.CallbackResultVo;
import com.xyy.saas.payment.cores.vo.ResultVO;
import com.xyy.saas.payment.cores.vo.SeparateAccountFromUserVo;
import com.xyy.saas.payment.dao.model.PayAccount;
import com.xyy.saas.payment.dao.model.ThirdUserInfo;
import com.xyy.saas.payment.merchant.core.dto.AccountOpenNoticeDtoV2;
import com.xyy.saas.payment.merchant.core.service.UserInfoService2;
import com.xyy.saas.payment.merchant.core.vo.UserInfoVo;
import com.xyy.saas.payment.service.PayAccountService;
import com.xyy.saas.payment.service.ThirdUserInfoService;
import com.xyy.saas.payment.settle.core.service.AccountService;
import com.xyy.saas.payment.util.DateUtil;
import com.xyy.saas.payment.util.JSONUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Objects;

@Component
@Slf4j
public class OpenAccountHandler implements BaseHandler {
    @Resource
    private UserInfoService2 userInfoService2;
    @Resource
    private CallbackRunner callbackRunner;
    @Resource
    private PayAccountService payAccountService;
    @Resource
    private PaymentOrderApiImpl paymentOrderApi;
    @Resource
    private ThirdUserInfoService thirdUserInfoService;
    @Autowired
    private AccountService accountService;

    /**
     * 方法
     */
    @Override
    public String getMethod() {
        return Constants.METHOD_OPEN_ACCOUNT_NAME;
    }

    /**
     * 处理回调
     */
    @Override
    public boolean callBackHandler(Object domain) {
        log.info("OpenAccountHandler#callBackHandler domain={}", JSONUtils.toJSON(domain));

        try {
            // 1.解析参数
            AccountOpenNoticeDtoV2 accountOpenNoticeDtoV2 = (AccountOpenNoticeDtoV2) domain;

            // 2.状态判断
            UserInfoVo userInfoVo = userInfoService2.queryUserInfo(accountOpenNoticeDtoV2.getAccountId());

            if(userInfoVo == null) {
                throw new PaymentException(ResultCodeEnum.USER_NOT_FOUND);
            }

            AccountCreateStatusEnum status = getStatus(accountOpenNoticeDtoV2.getApplicationStatus());

            if(Objects.equals(status.getStatus(), userInfoVo.getAuditStatus())) {
                log.warn("OpenAccountHandler#callBackHandler ignore, status not change. domain={}", JSONUtils.toJSON(domain));
                return true;
            }

            PayAccount payAccount = payAccountService.getPayAccountByAccountId(accountOpenNoticeDtoV2.getAccountId());
            String oldBusinessUserId = userInfoVo.getBasicInfo().getOldBusinessUserId();
            CallbackMsg callbackMsg = new CallbackMsg();
            callbackMsg.setTradeNo(payAccount.getBusinessUserId());
            callbackMsg.setCallback(userInfoVo.getSettleInfo().getCallbackAddress());
            callbackMsg.setExtend(oldBusinessUserId);

            // 3.更新状态
            switch(status) {
                case REJECT:
                    userInfoVo.setAuditStatus(status.getStatus());
                    userInfoVo.setMessage(accountOpenNoticeDtoV2.getAuditOpinion());
                    callbackMsg.setType(CallbackResultVo.ACCOUNT_OPEN_AUDIT_REJECT);
                    callbackMsg.setMsg(accountOpenNoticeDtoV2.getAuditOpinion());
                    break;
                case VERIFYING:
                    userInfoVo.setAuditStatus(status.getStatus());
                    userInfoVo.setMessage(accountOpenNoticeDtoV2.getProgressDescription());
                    callbackMsg.setCallback(CallbackConstants.NO_NOTIFY);
                    break;
                case SIGN:
                    userInfoVo.setAuditStatus(status.getStatus());
                    userInfoVo.setMessage(accountOpenNoticeDtoV2.getAgreementSignUrl());
                    callbackMsg.setType(CallbackResultVo.ACCOUNT_OPEN_AUDIT_PASS);
                    callbackMsg.setMsg(accountOpenNoticeDtoV2.getAgreementSignUrl());
                    break;
                case SUCCESS:
                    userInfoVo.setAuditStatus(status.getStatus());
                    userInfoVo.setMessage(status.getDesc());
                    callbackMsg.setType(CallbackResultVo.TYPE_OPEN_ACCOUNT_SUCCESS);
                    callbackMsg.setMsg(status.getDesc());
                    break;
            }

            userInfoVo.setUtime(DateUtil.now());
            userInfoService2.updateUserInfo(userInfoVo);

            // 4.尝试初始化账户余额
            if(AccountCreateStatusEnum.SUCCESS == status && StringUtils.isNotBlank(oldBusinessUserId)) {
                initBalanceFBank2Yop(payAccount.getBusinessIdType(), oldBusinessUserId, payAccount.getBusinessUserId());
            }

            // 5.发送通知
            callbackRunner.callback(callbackMsg);

            // 6.响应结果
            return true;
        }
        catch(Exception ex) {
            log.error("OpenAccountHandler#callBackHandler error domain=" + JSONUtils.toJSON(domain) + ", errorMsg=" + ex.getMessage() ,ex);
            return false;
        }
    }

    /**
     * 获取开户状态
     */
    private AccountCreateStatusEnum getStatus(String yopStatus) {
        // 驳回
        if("REVIEW_BACK".equals(yopStatus)) {
            return AccountCreateStatusEnum.REJECT;
        }
        // 真实性验证中
        else if("AUTHENTICITY_VERIFYING".equals(yopStatus)) {
            return AccountCreateStatusEnum.VERIFYING;
        }
        // 协议待签署
        else if("AGREEMENT_SIGNING".equals(yopStatus)) {
            return AccountCreateStatusEnum.SIGN;
        }
        // 已完成
        else if("COMPLETED".equals(yopStatus)) {
            return AccountCreateStatusEnum.SUCCESS;
        }

        throw new UnsupportedOperationException("Not supported yopStatus " + yopStatus);
    }

    /**
     * 初始化余额
     * 富民->易宝
     * @param businessIdType 商户类型
     * @param fBankBusinessUserId 富民商户标识
     * @param yopBusinessUserId 易宝商户标识
     */
    private void initBalanceFBank2Yop(String businessIdType, String fBankBusinessUserId, String yopBusinessUserId) throws PaymentException {
        log.error("OpenAccountHandler#initBalanceFBank2Yop businessIdType={}," +
                "fBankBusinessUserId={}, yopBusinessUserId={}", businessIdType, fBankBusinessUserId, yopBusinessUserId);

        // 0.初始化条件检验
        ThirdUserInfo fBankThirdUserInfo = thirdUserInfoService.
                getThirdUserInfoByUserId(payAccountService.getAccountIdByBusiness(businessIdType, fBankBusinessUserId));

        if(!Constants.QIFUTONG.equals(fBankThirdUserInfo.getThirdType())) {
            log.error("OpenAccountHandler#initBalanceFBank2Yop ignore 转出方非富民 businessIdType={}," +
                    "fBankBusinessUserId={}, yopBusinessUserId={}", businessIdType, fBankBusinessUserId, yopBusinessUserId);
            return;
        }

        ThirdUserInfo yopThirdUserInfo = thirdUserInfoService.
                getThirdUserInfoByUserId(payAccountService.getAccountIdByBusiness(businessIdType, yopBusinessUserId));

        if(!Constants.YOP.equals(yopThirdUserInfo.getThirdType())) {
            log.error("OpenAccountHandler#initBalanceFBank2Yop ignore 转入方非易宝 businessIdType={}," +
                    "fBankBusinessUserId={}, yopBusinessUserId={}", businessIdType, fBankBusinessUserId, yopBusinessUserId);
            return;
        }

        // 1.查询富民余额
        UserBo balance = accountService.queryAccountByBusinessId(fBankBusinessUserId, businessIdType, StringUtils.EMPTY);
        log.info("OpenAccountHandler#initBalanceFBank2Yop balance={}", JSONUtils.toJSON(balance));

        if(balance.getWithdrawableBalance() <= NumberUtils.INTEGER_ZERO) {
            return;
        }

        // 2.富民商户余额清空（商户->平台）
        SeparateAccountFromUserVo platform2Merchant = new SeparateAccountFromUserVo();
        platform2Merchant.setBusinessOrderNo(fBankBusinessUserId); // 商户标识作为转账单号
        platform2Merchant.setBusinessType(BusinessTypeEnum.YIKUAIQIAN.getType());
        platform2Merchant.setBusinessIdType(BusinessIdTypeEnum.YIKUAIQIAN_FM.getType());
        platform2Merchant.setBusinessOrderType(BusinessIdTypeEnum.YIKUAIQIAN_FM.getType());

        platform2Merchant.setBusinessUserIdOut(fBankBusinessUserId);
        platform2Merchant.setBusinessUserIdIn("yikuaiqian_fm_platform");
        platform2Merchant.setAmount(balance.getWithdrawableBalance().intValue());

        platform2Merchant.setInvoker(StringUtils.EMPTY);
        platform2Merchant.setExtra("富民商户余额清空（商户->平台）");
        ResultVO<SeparateAccountsBo> fBankTransfer = paymentOrderApi.separateAccountFromUser(platform2Merchant, StringUtils.EMPTY);

        if(!ResultCodeEnum.SUCCESS.getCode().equals(fBankTransfer.getCode())) {
            throw new PaymentException(fBankTransfer.getDisplayMsg());
        }

        // 3.易宝商户余额初始化（平台->商户）
        SeparateAccountFromUserVo merchant2Platform = new SeparateAccountFromUserVo();
        merchant2Platform.setBusinessOrderNo(yopBusinessUserId); // 商户标识作为转账单号
        merchant2Platform.setBusinessType(BusinessTypeEnum.YIKUAIQIAN.getType());
        merchant2Platform.setBusinessIdType(BusinessIdTypeEnum.YIKUAIQIAN_FM.getType());
        merchant2Platform.setBusinessOrderType(BusinessIdTypeEnum.YIKUAIQIAN_FM.getType());

        merchant2Platform.setBusinessUserIdOut("yikuaiqian_fm_platform_yop");
        merchant2Platform.setBusinessUserIdIn(yopBusinessUserId);
        merchant2Platform.setAmount(balance.getWithdrawableBalance().intValue());

        merchant2Platform.setInvoker(StringUtils.EMPTY);
        merchant2Platform.setExtra("易宝商户余额初始化（平台->商户）");
        ResultVO<SeparateAccountsBo> yopTransfer = paymentOrderApi.separateAccountFromUser(merchant2Platform, StringUtils.EMPTY);

        if(!ResultCodeEnum.SUCCESS.getCode().equals(yopTransfer.getCode())) {
            throw new PaymentException(yopTransfer.getDisplayMsg());
        }
    }
}
